package com.alibaba.csp.sentinel.dashboard.rule;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * RuleRepository的切面处理程序
 *
 * @author slf
 * @since 2024/4/14 17:20
 **/
@Aspect
@Component
public class DynamicRuleAspect {
  private final Logger log = LoggerFactory.getLogger(DynamicRuleAspect.class);

  /**
   * 查询切点中要处理的方法
   */
  public final static Map<String, Class> METHODS = new HashMap<>(16);

  static {
    METHODS.put("fetchDegradeRuleOfMachine", DegradeRuleEntity.class);
    METHODS.put("fetchSystemRuleOfMachine", SystemRuleEntity.class);
    METHODS.put("fetchGatewayFlowRules", GatewayFlowRuleEntity.class);
    METHODS.put("fetchFlowRuleOfMachine", FlowRuleEntity.class);
    METHODS.put("fetchParamFlowRulesOfMachine", ParamFlowRuleEntity.class);
    METHODS.put("fetchAuthorityRulesOfMachine", AuthorityRuleEntity.class);
    METHODS.put("fetchApis", ApiDefinitionEntity.class);
  }

  @Resource
  DynamicRuleHandler handler;

  /**
   * 推送规则切点
   */
  @Pointcut("execution( com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter.save(com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity))")
  public void save() {
  }

  /**
   * 推送规则切点
   */
  @Pointcut("execution( com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter.delete(java.lang.Long))")
  public void delete() {
  }

  @Around("save()||delete()")
  public Object saveAround(ProceedingJoinPoint point) throws Throwable {
    String name = point.getSignature().getName();
    RuleEntity entity;
    Object[] args = point.getArgs();
    InMemoryRuleRepositoryAdapter<RuleEntity> repository = (InMemoryRuleRepositoryAdapter<RuleEntity>) point.getTarget();
    if ("delete".equals(name)) {
      entity = repository.findById((Long) args[0]);
    } else {
      entity = (RuleEntity) args[0];
    }
    Object result = point.proceed(args);
    try {
      List rules = repository.findAllByApp(entity.getApp());
      if (rules != null) {
        Class clazz = entity.getClass();
        handler.publish(entity.getApp(), clazz, rules);
      }
    } catch (Exception e) {
      log.error("推送失败：{}:{}", name, args, e);
    }
    return result;
  }

  /**
   * 查询规则切点
   */
  @Pointcut("execution( * com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient.fetch*(..))")
  public void fetch() {
  }

  @Around(value = "fetch()")
  public Object fetchAround(ProceedingJoinPoint point) {
    log.debug("查询执行");

    Object[] args = point.getArgs();
    Signature signature = point.getSignature();

    String method = signature.getName();
    ArrayList<RuleEntity> rules = null;
    if (METHODS.containsKey(method)) {
      rules = handler.getRules((String) args[0], METHODS.get(method));
    }
    try {
      if (rules == null) {
        return point.proceed(args);
      }
    } catch (Throwable e) {
      log.error("查询异常：", e);
    }
    Class declaringType = signature.getDeclaringType();
    if (CompletableFuture.class.equals(declaringType)) {
      CompletableFuture<List<RuleEntity>> future = new CompletableFuture<>();
      future.complete(rules);
      return future;
    }
    return rules;
  }

}
